#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

#include "utils.h"


int add_matrix(struct matrix_t* a, struct matrix_t* b);

int main(int argc, char** argv)
{
	struct MPI_Info info;
	struct matrix_t* mat;
	struct matrix_t* smat;
	struct matrix_t* ans_mat;
	struct matrix_t* aux_mat;
	long* mem_w;
	long* rows, rows_sz;
	long* b_col;
	long* ans;
	long i, j, k;
	long chunk_size, a_rows, b_rows, b_cols;
	MPI_Info mpinfo;
	MPI_Win wind;
	long matrix_info[3];

	MPI_Init(&argc, &argv);
	get_mpi_info(&info);

	if (info.proc_id == 0) {
		read_matrix("A.txt", &mat);
		read_matrix("B.txt", &smat);
		if (mat->n != smat->m) exit(-1);
		alloc_matrix(&ans_mat, mat->m, smat->n);

		matrix_info[0] = mat->m;
		matrix_info[1] = mat->n;
		matrix_info[2] = smat->n;
		MPI_Bcast(matrix_info, 3, MPI_LONG, 0, MPI_COMM_WORLD);

		MPI_Win_create(mem_w, 0, sizeof(long), mpinfo, MPI_COMM_WORLD, &wind);
		
		for (i = 0; i < mat->m; ++i) {
			j = i % info.num_procs;	// Calcula el proc destino.
			if (j != 0) {
				MPI_Win_fence(0, wind);
				mem_w = (mat->mat) + (i * mat->n);
				MPI_Put(
					(void*) mem_w,
					mat->n,
					MPI_LONG,
					j,
					(i/info.num_procs)*mat->n,
					mat->n,
					MPI_LONG,
					wind);
				MPI_Win_fence(0, wind);
			}
		}

		MPI_Win_free(&wind);
	} else {
		MPI_Bcast(matrix_info, 3, MPI_LONG, 0, MPI_COMM_WORLD);
		rows_sz = matrix_info[0] / info.num_procs;
		rows_sz += (matrix_info[0] % info.num_procs) > info.proc_id ? 1 : 0;
		rows_sz *= matrix_info[1];

		mem_w = (long*) malloc(sizeof(long)*rows_sz);
		rows = (long*) malloc(sizeof(long)*rows_sz);
		MPI_Win_create(
			(void*) mem_w, 
			rows_sz, 
			sizeof(long), 
			mpinfo, 
			MPI_COMM_WORLD,
			&wind);

		for (i = 0; i < matrix_info[0]; ++i) {
			if ((i % info.num_procs) != 0) {
				MPI_Win_fence(0, wind);
				MPI_Win_fence(0, wind);
			}
		}

		MPI_Win_free(&wind);
	}
			
	MPI_Barrier(MPI_COMM_WORLD);
	MPI_Finalize();

	return 0;
}
